package vg.services.graph_view_manager.realization.graph_comparison;

import javax.swing.*;

import vg.services.data_base_manager.data.graph.Graph;
import vg.services.graph_view_manager.interfaces.graph_comparison.*;
import vg.services.graph_view_manager.interfaces.graph_view.AGraphView;
import vg.services.graph_view_manager.interfaces.graph_view.IGraphRenderer;
import vg.services.graph_view_manager.realization.graph_comparison.algorithm.GraphComparisonAlgorithm;
import vg.services.graph_view_manager.realization.graph_view.smart_graph_view.realization.actions.ExecuteLayoutAction;
import vg.services.graph_view_manager.realization.graph_view.smart_graph_view.realization.actions.NewGraphAction;
import vg.services.graph_view_manager.realization.graph_view.smart_graph_view.realization.actions.SetColorAction;
import vg.services.graph_view_manager.realization.graph_view.smart_graph_view.realization.actions.SetVisibleAction;
import vg.services.main_manager.MainManager;

import java.awt.*;

/**
 * Graph comparison implementation.
 *
 * @author Timur Zolotuhin (e-mail: tzolotuhin@gmail.com)
 */
public class SmartGraphComparisonView extends AGraphComparisonView {
	// Main data
	private GraphComparisonAlgorithm graphComparisonAlgorithm;
    private AGraphView currentGraphView;
    private JPanel view;
    private String title;

    private boolean startComparisonProcess = false;

	// Mutex
	private final Object generalMutex = new Object();
	
	/**
	 * Constructor.
	 * @param g1 - first graph
	 * @param g2 - second graph
	 */
	public SmartGraphComparisonView(Graph g1, Graph g2) {
		graphComparisonAlgorithm = new GraphComparisonAlgorithm(g1, g2);
        String title1 = MainManager.graphDataBase.generateTitle4Graph(g1);
        String title2 = MainManager.graphDataBase.generateTitle4Graph(g2);
        title = title1 + " vs " + title2;

        init();
	}
	
	@Override
	public String getTabTitle() {
        synchronized (generalMutex) {
            return title;
        }
	}
	
	@Override
	public void setTabTitle(String title) {
		synchronized (generalMutex) {
            this.title = title;
        }
	}
	
	@Override
	public JComponent getView() {
        synchronized (generalMutex) {
            return view;
        }
	}

    @Override
    public boolean compare(final IGraphComparisonAlgorithmObserver observer) {
        synchronized (generalMutex) {
            if (startComparisonProcess)
                return false;

            startComparisonProcess = true;

            Thread t = new Thread(new Runnable() {
                @Override
                public void run() {
                    graphComparisonAlgorithm.execute(observer);

                    SwingUtilities.invokeLater(new Runnable() {
                        @Override
                        public void run() {
                            synchronized (generalMutex) {
                                startComparisonProcess = false;
                                OutputGraphComparisonAlgorithmProperties output = graphComparisonAlgorithm.getOutputGraphComparisonAlgorithmProperties();
                                Graph mcs = output.getMaximalCommonSubgraph();

                                currentGraphView.addAction(new NewGraphAction(mcs));
                                currentGraphView.addAction(new ExecuteLayoutAction() {
                                    @Override
                                    public void finishAction(IGraphRenderer graphRenderer) {
                                        show(SHOW_GRAPH_INTERSECTION);
                                    }
                                });


                            }
                        }
                    });
                }
            });
            t.setDaemon(true);
            t.start();
            return true;
        }
    }

    @Override
    public void stopComparison() {
        synchronized (generalMutex) {
            if (startComparisonProcess)
                graphComparisonAlgorithm.stopAnyPhase();
        }
    }

    @Override
    public boolean isStartComparisonProcess() {
        synchronized (generalMutex) {
            return startComparisonProcess;
        }
    }

    @Override
    public boolean setInputGraphComparisonAlgorithmProperties(InputGraphComparisonAlgorithmProperties properties) {
        synchronized (generalMutex) {
            if (!startComparisonProcess) {
                graphComparisonAlgorithm.setInputGraphComparisonAlgorithmProperties(properties);
                return true;
            }
            return false;
        }
    }

    @Override
    public InputGraphComparisonAlgorithmProperties getCurrentInputGraphComparisonAlgorithmProperties() {
        synchronized (generalMutex) {
            if (!startComparisonProcess)
                return graphComparisonAlgorithm.getInputGraphComparisonAlgorithmProperties();
            return null;
        }
    }

    @Override
    public OutputGraphComparisonAlgorithmProperties getOutputGraphComparisonAlgorithmProperties() {
        synchronized (generalMutex) {
            if (!startComparisonProcess)
                return graphComparisonAlgorithm.getOutputGraphComparisonAlgorithmProperties();
            return null;
        }
    }

    @Override
    public OutputGraphComparisonAlgorithmProperties estimate(InputGraphComparisonAlgorithmProperties properties) {
        synchronized (generalMutex) {
            return graphComparisonAlgorithm.estimate(properties);
        }
    }

    @Override
    public boolean show(int part) {
        synchronized (generalMutex) {
            if (!startComparisonProcess && currentGraphView != null) {
                // paint elements
                OutputGraphComparisonAlgorithmProperties output = graphComparisonAlgorithm.getOutputGraphComparisonAlgorithmProperties();
                currentGraphView.addAction(new SetColorAction(Color.RED, output.getFirstGraphVertexOutsiders(), output.getFirstGraphEdgeOutsiders()));
                currentGraphView.addAction(new SetColorAction(Color.BLUE, output.getSecondGraphVertexOutsiders(), output.getSecondGraphEdgeOutsiders()));

                // select main elements
                switch(part) {
                    case SHOW_GRAPH_INTERSECTION: {
                        currentGraphView.addAction(new SetVisibleAction(SetVisibleAction.INVISIBLE_ACTION, output.getFirstGraphVertexOutsiders(), output.getFirstGraphEdgeOutsiders()));
                        currentGraphView.addAction(new SetVisibleAction(SetVisibleAction.INVISIBLE_ACTION, output.getSecondGraphVertexOutsiders(), output.getSecondGraphEdgeOutsiders()));
                        break;
                    }

                    case SHOW_FIRST_GRAPH: {
                        currentGraphView.addAction(new SetVisibleAction(SetVisibleAction.VISIBLE_ACTION, output.getFirstGraphVertexOutsiders(), output.getFirstGraphEdgeOutsiders()));
                        currentGraphView.addAction(new SetVisibleAction(SetVisibleAction.INVISIBLE_ACTION, output.getSecondGraphVertexOutsiders(), output.getSecondGraphEdgeOutsiders()));
                        break;
                    }

                    case SHOW_SECOND_GRAPH: {
                        currentGraphView.addAction(new SetVisibleAction(SetVisibleAction.INVISIBLE_ACTION, output.getFirstGraphVertexOutsiders(), output.getFirstGraphEdgeOutsiders()));
                        currentGraphView.addAction(new SetVisibleAction(SetVisibleAction.VISIBLE_ACTION, output.getSecondGraphVertexOutsiders(), output.getSecondGraphEdgeOutsiders()));
                        break;
                    }

                    default: {
                        break;
                    }
                }

                return true;
            }
            return false;
        }
    }

    @Override
    public AGraphView getMainGraphView() {
        synchronized (generalMutex) {
            return currentGraphView;
        }
    }

//==============================================================================
//------------------PRIVATE METHODS---------------------------------------------
    private void init() {
        // initialize draw components
        if (!SwingUtilities.isEventDispatchThread()) {
            try {
                SwingUtilities.invokeAndWait(new Runnable() {
                    @Override
                    public void run() {
                        init();
                    }
                });
            } catch (Exception ex) {
                MainManager.logger.printException(ex);
            }
            init();
        } else {
            view = new JPanel(new BorderLayout());
            currentGraphView = MainManager.graphViewManager.newGraphView();
            view.add(currentGraphView.getView());
        }
    }
}
